﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace PrototypeZGame
{
    public class HumanAI : AIController
    {
        private enum State
        {
            Sleeping,
            Resting,
            Searching,
            Running,
            Barricading,
            Fighting,
            Dead,
        }

        private enum TargetType
        {
            Food,
            Weapon,
            Field,
            Zombie,
        }

        TargetType targetType;
        State state;
        public Human human;

        public HumanAI(World world)
            : base(world)
        {
            state = State.Searching;
        }

        private Actor findNewTarget()
        {
            Actor ret;
            if (unit.inventory.rightHand == null)
            {
                ret = findNewWeapon();
                if (ret != null)
                    return ret;
            }

            if (unit.inventory.Count < unit.inventory.capacity - 1)
            {
                ret = findFood();
                if (ret != null)
                    return ret;
            }

            return findNewPlace();
        }

        private Food findFood()
        {
            List<Food> food = human.findFood();
            if (food != null && food.Count > 0)
            {
                Food best = food[0];
                foreach (Food f in food)
                {
                    if (World.Distance(f, unit) < World.Distance(best, unit))
                    {
                        best = f;
                    }
                }
                targetType = TargetType.Food;
                return best;
            }
            else
            {
                return null;
            }
        }
        
        private Weapon findNewWeapon()
        {
            List<Weapon> weapons = human.findWeapon();
            if (weapons != null && weapons.Count > 0)
            {
                float max = 0, w;
                Weapon best = weapons[0];
                foreach (Weapon weapon in weapons)
                {
                    w = weapon.damage / weapon.EC;
                    if (w > max)
                    {
                        max = w;
                        best = weapon;
                    }
                    else if (w == max)
                    {
                        if (World.Distance(weapon, unit) < World.Distance(best, unit))
                        {
                            best = weapon;
                        }
                    }
                }
                targetType = TargetType.Weapon;
                return best;
            }
            else
            {
                return null;
            }
        }

        private Field findNewPlace()
        {
            Vec2 v;
            bool c = false;

            int range = (int)(unit.viewDistance);

            do
            {
                v = unit.position + new Vec2(world.rand.Next(2 * range) - range, world.rand.Next(2 * range) - range);
                c = terrain.isPositionValid(v);
            } while (!c);

            targetType = TargetType.Field;
            return terrain.field[v.x, v.y];
        }

        public override void newTurn()
        {
            unit.newTurn();
            


            switch (state)
            {
                case State.Searching:
                    human.evaluateEnemiesStrength();
                    if (unit.inventory.rightHand != null)
                    {
                        Zombie retZ = FindWeekestZombieInRange();
                        if (retZ != null)
                        {
                            unit.Attack(retZ);
                        }
                    }
                    if (target == null)
                    {
                        target = findNewTarget();
                    }

                    if (target != null)
                    {
                        
                        if (World.Distance(unit, target) <= 0.5)
                        {
                            if (targetType == TargetType.Weapon)
                            {
                                unit.takeItem((InventoryItem)target);
                                chooseBestWeapon();
                            }
                            else if (targetType == TargetType.Food)
                            {
                                unit.takeItem((InventoryItem)target);
                            }
                            target = null;
                        }
                        else
                        {
                            if (human.maxFL - human.FL > 1200)
                            {
                                if (eat())
                                {
                                    break;
                                }
                            }
                            Result result = MoveToTarget((int)unit.viewDistance + 2);
                            if (result == Result.BlockingDoor)
                            {
                                openDoor();
                            }
                            else if (result == Result.BlockingZombie)
                            {
                                attackZombie();
                            }
                            else if (result == Result.NoPath)
                            {
                                target = null;
                            }
                        }

                    }

                    break;
                case State.Fighting:
                    break;
                case State.Dead:
                    break;
            }
        }

        private Zombie FindWeekestZombieInRange()
        {
            Vec2 a = unit.position - new Vec2(1);
            Vec2 b = unit.position + new Vec2(1);
            Zombie ret = null;
            for (Vec2 v = a; v.x <= b.x; v.x++)
            {
                for (v.y = a.y; v.y <= b.y; v.y++)
                {
                    if (terrain.isPositionValid(v))
                    {
                        foreach (Zombie zombie in terrain.getField(v).onFieldZombies)
                        {

                            // TODO: fix
                            if(!zombie.dead && ret == null){
                                ret = zombie;
                            } else if(!zombie.dead && zombie.HP> ret.HP){
                                ret = zombie;
                            }
                        }
                    }
                }
            }

            return ret;
        }

        private bool eat()
        {
            int i = 0;
            for (; i < unit.inventory.Count; i++)
            {
                Food food = unit.inventory.at(i) as Food;
                if (food != null)
                {
                    human.useItem(i);
                    return true;
                }
            }
            return false;
        }

        private void chooseBestWeapon()
        {
            int bestIndex = -1;
            float max = 0, w;
            for (int i = 0; i < unit.inventory.Count; i++)
            {
                Weapon weapon = unit.inventory.at(i) as Weapon;
                if (weapon != null)
                {
                    w = weapon.damage / weapon.EC;
                    if (w > max)
                    {
                        max = w;
                        bestIndex = i;
                    }
                }
            }

            if (bestIndex == -1)
            {
                unit.inventory.rightHand = null;
            }
            else
            {
                unit.useItem(bestIndex);
            }
        }

        private void attackZombie()
        {
            Vec2 targetPosition = unit.position + World.DirToVec2(path[0]);
            Field targetField = terrain.getField(targetPosition);
            List<Zombie> zombies = targetField.onFieldZombies;
            foreach (Zombie z in zombies)
            {
                if (!z.dead)
                {
                    unit.Attack(z);
                }
            }
            
        }

        private void openDoor()
        {
            Vec2 targetPosition = unit.position + World.DirToVec2(path[0]);
            Field targetField = terrain.getField(targetPosition);
            List<Item> items = targetField.onFieldItems;
            foreach (Item item in items)
            {
                Door door = item as Door;
                if (door != null)
                {
                    unit.open(door);
                    break;
                }
            }
        }

        public override void UnitDied()
        {
            state = State.Dead;
        }

        public override void Posses(Unit _unit)
        {
            base.Posses(_unit);
            Human _human = _unit as Human;
            if (_human != null)
            {
                human = _human;
            }
        }

        public override void UnPosses()
        {
            base.UnPosses();
            human = null;
        }


    }
}
